package com.fary.utils;

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Map;

/**
 * JWT Token工具类
 *
 */
@Slf4j
public class JwtUtil {

    private static final String tokenHead = "WebType ";
    private static final Long overTimestamp = 7200000L;
    private static final String SECRET = "0ZLsy4Od597bS8Iu6u39F5N9Wumh6VFei1UHc0xDGN3gKHwlssvoTKyXNdNAKxnJltRcd2sv0PjWBDcLKk06e0xrM5HhAwkPr01w";
    private static final SignatureAlgorithm HS256 = SignatureAlgorithm.HS256;
    private static final SignatureAlgorithm HS512 = SignatureAlgorithm.HS512;

    /**
     * 生成签名私钥Key
     *
     * @return
     */
    private static SecretKey generalKey(SignatureAlgorithm sign) {
        byte[] encodedKey = DatatypeConverter.parseBase64Binary(SECRET);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, sign.getJcaName());
        return key;
    }

    /**
     * 生成随机字符
     *
     * @param length
     * @return
     */
    public static String generalRandomString(int length) {
        SecureRandom secureRandom = new SecureRandom();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = secureRandom.nextInt(3);
            long result = 0;
            switch (number) {
                case 0:
                    result = Math.round(secureRandom.nextDouble() * 25 + 65);
                    sb.append(String.valueOf((char) result));
                    break;
                case 1:
                    result = Math.round(secureRandom.nextDouble() * 25 + 97);
                    sb.append(String.valueOf((char) result));
                    break;
                case 2:
                    sb.append(String.valueOf(secureRandom.nextInt(10)));
                    break;
                default:
                    break;
            }
        }
        return sb.toString();
    }

    /**
     * 生成过期时间
     *
     * @return
     */
    private static Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + overTimestamp);
    }

    /**
     * 生成Jwt令牌，带过期时间
     * HS256
     *
     * @param claims
     * @return
     */
    public static String generateHS256JwtHasExpiration(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(generateExpirationDate())
                .signWith(HS256, generalKey(HS256)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌，带过期时间
     * HS512
     *
     * @param claims
     * @return
     */
    public static String generateHS512JwtHasExpiration(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(generateExpirationDate())
                .signWith(HS512, generalKey(HS512)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌
     * HS256
     *
     * @param claims
     * @return
     */
    public static String generateHS256Jwt(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).signWith(HS256, generalKey(HS256)).compact();
        return tokenHead + jwt;
    }

    /**
     * 生成Jwt令牌
     * HS512
     *
     * @param claims
     * @return
     */
    public static String generateHS512Jwt(Map<String, Object> claims) {
        Date now = new Date(System.currentTimeMillis());
        String jwt = Jwts.builder().setClaims(claims).setIssuedAt(now).signWith(HS512, generalKey(HS512)).compact();
        return tokenHead + jwt;
    }

    /**
     * 解析Jwt字符串
     * HS256
     *
     * @param jwt
     * @return
     */
    public static Claims parseHS256Jwt(String jwt) {
        Claims claims = null;
        try {
            if (StrUtil.isNotBlank(jwt)) {
                claims = Jwts.parser().setSigningKey(generalKey(HS256)).parseClaimsJws(jwt.substring(tokenHead.length())).getBody();
            } else {
                log.warn("jwt为空");
            }
        } catch (ExpiredJwtException eje) {
            claims = eje.getClaims();
            log.error("jwt过期");
        } catch (Exception e) {
            claims = null;
            log.error("解析jwt异常：", e);
        }
        return claims;
    }

    /**
     * 解析Jwt字符串
     * HS512
     *
     * @param jwt
     * @return
     */
    public static Claims parseHS512Jwt(String jwt) {
        Claims claims = null;
        try {
            if (StrUtil.isNotBlank(jwt)) {
                claims = Jwts.parser().setSigningKey(generalKey(HS512)).parseClaimsJws(jwt.substring(tokenHead.length())).getBody();
            } else {
                log.warn("jwt为空");
            }
        } catch (ExpiredJwtException eje) {
            claims = eje.getClaims();
            log.error("jwt过期");
        } catch (Exception e) {
            claims = null;
            log.error("解析jwt异常：", e);
        }
        return claims;
    }

    /**
     * 刷新Jwt字符串   带过期时间
     * HS256
     *
     * @param jwt
     * @return
     */
    public static String refreshHS256JwtHasExpiration(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS256Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS256JwtHasExpiration(claims);
            }
        } catch (Exception e) {
            log.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串   带过期时间
     * HS512
     *
     * @param jwt
     * @return
     */
    public static String refreshHS512JwtHasExpiration(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS512Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS512JwtHasExpiration(claims);
            }
        } catch (Exception e) {
            log.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串
     * HS256
     *
     * @param jwt
     * @return
     */
    public static String refreshHS256Jwt(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS256Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS256Jwt(claims);
            }
        } catch (Exception e) {
            log.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 刷新Jwt字符串
     * HS512
     *
     * @param jwt
     * @return
     */
    public static String refreshHS512Jwt(String jwt) {
        String refreshedJwt = null;
        Claims claims;
        try {
            claims = parseHS512Jwt(jwt);
            if (null != claims) {
                claims.setIssuedAt(new Date());
                refreshedJwt = generateHS512Jwt(claims);
            }
        } catch (Exception e) {
            log.error("获取新JWT异常", e);
            refreshedJwt = null;
        }
        return refreshedJwt;
    }

    /**
     * 判断Token是否过期
     *
     * @param claims
     * @return false: 过期
     */
    public static boolean isExpire(Claims claims) {
        Date expiration = claims.getExpiration();
        return expiration.before(new Date());
    }
}
